﻿#if false
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Util.IO
{
    /// <summary>
    /// Reads data compressed using using run-length encoding.
    /// </summary>
    /// <remarks>
    /// The compressed data are stored in packets, where each packet starts
    /// with a byte header, n, defined below:
    /// - If n is between 0 and 127, (n + 1) number of elements that follow
    ///   are copied as is.
    /// - If n is between 128 and 255, the following element is repeated 
    ///   (n - 127) times.
    /// This arrangement is the same as TGA spec.
    /// </remarks>
    public class RunLengthDecoder : Stream
    {
        private int elemSize;  // element size: 1 for byte, 2 for word, etc.
        private Stream baseStream;
        private byte[] cache;  // cached bytes
        private int cacheSize;
        private int cacheStart;

        public RunLengthDecoder(Stream inputStream) : this(inputStream, 1) { }

        public RunLengthDecoder(Stream inputStream, int elementSize)
        {
            if (elementSize <= 0)
                throw new ArgumentException("elemSize must be greater than zero.");
            this.elemSize = elementSize;
            this.baseStream = inputStream;
            this.cache = new byte[elemSize * 128];
            this.cacheSize = 0;
            this.cacheStart = 0;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && baseStream != null)
            {
                baseStream.Dispose();
                baseStream = null;
                cache = null;
                cacheStart = cacheSize = 0;
            }
            base.Dispose(disposing);
        }

        public override bool CanRead { get { return true; } }

        public override bool CanWrite { get { return false; } }

        public override bool CanSeek { get { return false; } }

        private bool FillCache()
        {
            // Read one byte.
            int n = baseStream.ReadByte();
            if (n < 0) // EOF
                return false;

            int repeat = (n & 0x7F) + 1;
            if ((n & 0x80) != 0) // RLE
            {
                // Read one element and repeat it 'repeat' times.
                baseStream.ReadFull(cache, 0, elemSize);
                for (int i = 1; i < repeat; i++)
                {
                    Array.Copy(cache, 0, cache, i * elemSize, elemSize);
                }
            }
            else // raw data
            {
                // Read 'repeat' bytes as is.
                baseStream.ReadFull(cache, 0, repeat * elemSize);
            }
            cacheStart = 0;
            cacheSize = elemSize * repeat;
            return true;
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            if (cacheStart == cacheSize)
            {
                if (!FillCache())
                    return 0;
            }
            int n = Math.Min(count, cacheSize - cacheStart);
            Array.Copy(cache, cacheStart, buffer, offset, n);
            cacheStart += n;
            return n;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        public override void Flush()
        {
            throw new NotSupportedException();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override long Position
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        public override long Length
        {
            get { throw new NotSupportedException(); }
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }
    }

    public class RunLengthEncoder : Stream
    {
        private int elemSize;  // element size: 1 for byte, 2 for word, etc.
        private Stream baseStream;
        private byte[] cache;  // cached bytes
        private int cacheSize;

        public RunLengthEncoder(Stream baseStream) : this(baseStream, 1) { }

        public RunLengthEncoder(Stream outputStream, int elementSize)
        {
            if (elementSize <= 0)
                throw new ArgumentException("elemSize must be greater than zero.");
            this.elemSize = elementSize;
            this.baseStream = outputStream;
            this.cache = new byte[elemSize * 128];
            this.cacheSize = 0;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing && baseStream != null)
            {
                FlushCache(true);
                baseStream.Dispose();
                baseStream = null;
                cache = null;
                cacheSize = 0;
            }
            base.Dispose(disposing);
        }

        public override bool CanRead { get { return false; } }

        public override bool CanWrite { get { return true; } }

        public override bool CanSeek { get { return false; } }

        private void FlushCache(bool flushAll)
        {
            // Don't do anything until the cache is full or Flush() is called.
            while (cacheSize > 0 && (cacheSize == cache.Length || flushAll))
            {
                if (cacheSize < elemSize)
                {
                    throw new InvalidOperationException(
                        "Input must be an integral number of elements.");
                }

                // Check if we start with a repeating or non-repeating packet.
                // We start with a repeating packet if the cache contains two
                // or more elements and the first two elements are equal.
                bool startEqual = (cacheSize > elemSize) &&
                    CompareEqual(cache, 0, elemSize, elemSize);

                if (startEqual) // encode RLE packet
                {
                    int repeat = GetRepeatCount(cache, 0, cacheSize, elemSize);
                    int byteCount = repeat * elemSize;

                    // Encode RLE packet.
                    baseStream.WriteByte((byte)(0x80 | (repeat - 1)));
                    baseStream.Write(cache, 0, elemSize);

                    // Move the encoded elements.
                    cacheSize -= byteCount;
                    Array.Copy(cache, byteCount, cache, 0, cacheSize);
                }
                else // encode RAW packet
                {
                    int nonrepeat = GetNonRepeatCount(cache, 0, cacheSize, elemSize);
                    int byteCount = nonrepeat * elemSize;

                    // Encode the raw packet.
                    baseStream.WriteByte((byte)(nonrepeat - 1));
                    baseStream.Write(cache, 0, byteCount);

                    // Move the encoded elements.
                    cacheSize -= byteCount;
                    Array.Copy(cache, byteCount, cache, 0, cacheSize);
                }
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            while (count > 0)
            {
                if (cacheSize == cache.Length)
                    FlushCache(false);

                int n = Math.Min(count, cache.Length - cacheSize);
                Array.Copy(buffer, offset, cache, cacheSize, n);
                offset += n;
                count -= n;
                cacheSize += n;
            }
        }

        public override void Flush()
        {
            FlushCache(true);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException();
        }

        public override long Position
        {
            get { throw new NotSupportedException(); }
            set { throw new NotSupportedException(); }
        }

        public override long Length
        {
            get { throw new NotSupportedException(); }
        }

        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Counts the number of following elements (not bytes) equal to the
        /// element at the given byte offset.
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        /// <param name="elemSize"></param>
        /// <returns></returns>
        public static int GetRepeatCount(byte[] buffer, int offset, int count, int elemSize)
        {
            int i = 0;
            while (i < count - elemSize &&
                   buffer[offset + i] == buffer[offset + i + elemSize])
                i++;
            return i / elemSize + 1;
        }

        public static bool CompareEqual(byte[] buffer, int index1, int index2, int count)
        {
            for (int j = 0; j < count; j++)
            {
                if (buffer[index1 + j] != buffer[index2 + j])
                    return false;
            }
            return true;
        }

        /// <summary>
        /// Counts the number of elements (not bytes) until two consecutive
        /// elements are equal. For example, (12 13 14 14) returns 2.
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        /// <param name="elemSize"></param>
        /// <returns></returns>
        public static int GetNonRepeatCount(
            byte[] buffer, int offset, int count, int elemSize)
        {
            int i;
            for (i = 0; i < count - elemSize; i += elemSize)
            {
                if (CompareEqual(buffer, offset + i, offset + i + elemSize, elemSize))
                    return i / elemSize;
            }
            return count / elemSize;
        }
    }

#if false
    public class RunLengthPacket<T>
    {
        public T[] Data; // encoded data (without meta-data)
        public int[] Repeat; // > 0 = repeat element that many times
                             // < 0 = copy that many elements as is
    }

    public class RunLengthEncoding<T>
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="threshold">Create a repeat packet only if the 
        /// number of repetition is greater than threshold.</param>
        public void Encode(T[] buffer, int threshold)
        {
            int lastIndex = 0;
            T last = buffer[0];
            for (int i = 1; i < buffer.Length; i++)
            {
                int n;
                int j = i;
                while (EqualityComparer<T>.Default.Equals(buffer[i], last))
                    j++;
                n = j - i;
                if (n <= threshold)
                    ; // do nothing

                // Otherwise
                RunLengthPacket<T> packet = new RunLengthPacket<T>();
                packet.Data = buffer;
                packet.Repeat = n;
            }
        }
    }
#endif
}
#endif
